﻿using CommunityToolkit.Maui.Core.Primitives;
using CommunityToolkit.Maui.Views;
using Elio.Controls;
using Google.Protobuf.WellKnownTypes;

namespace Elio.Sessions.Media;

public class MediaPlayerSession : BaseSession
{
    protected override string SessionName => base.SessionName = "MediaPlayer";

    private MediaElement? _media;

    protected override async Task OnCreate(long sid = 0, long fid = 0, params Any[] argv)
    {
        // 必须添加到界面，才能响应播放源的状态更改
        _media = new MediaElement() { WidthRequest = 0, HeightRequest = 0 };
        _media.SetPoint(0, 0);
        _media.IsVisible = false;
        Parent!.ElegantPage!.Views.Add(_media);
        await base.OnCreate(sid, fid, argv);
    }

    protected override async Task OnDestroy()
    {
        _media!.Stop();
        Parent?.ElegantPage?.Views.Remove(_media!);
        await base.OnDestroy();
    }

    protected override async Task OnReceive(string name, long sessionId, long futureId, params Any[] argv)
    {
        switch (name)
        {
            case "load":
                await Load(sessionId, futureId, argv);
                break;

            case "loadFromWidgetFileUploader":
                LoadFromWidgetFileUploader();
                break;

            case "loadFromMediaRecorder":
                LoadFromMediaRecorder();
                break;

            case "stop":
                Stop();
                break;

            case "play":
                Play();
                break;

            case "pause":
                Pause();
                break;

            case "isPlaying":
                await IsPlaying(sessionId, futureId);
                break;

            case "duration":
                await Duration(sessionId, futureId);
                break;

            case "currentPosition":
                await CurrentPosition(sessionId, futureId);
                break;

            case "isLooping":
                await IsLooping(sessionId, futureId);
                break;

            case "setLooping":
                SetLooping(argv);
                break;

            case "setSpeed":
                SetSpeed(argv);
                break;

            case "setVolume":
                SetVolume(argv);
                break;

            case "seekTo":
                SeekTo(argv);
                break;

            case "setOnCompletionListener":
                SetOnCompletionListener();
                break;

            default:
                await base.OnReceive(name, sessionId, futureId, argv);
                break;
        }
    }

    private async Task Load(long sid, long fid, params Any[] argv)
    {
        // 不设置或者设置false都会出现异常
        _media!.ShouldAutoPlay = true;
        _media.Source = new Uri(argv[0].P2String());
        _media.Pause();
        await SendAction("onLoad", sid, fid, argv[0]);
    }

    private static void LoadFromWidgetFileUploader() { }

    private static void LoadFromMediaRecorder() { }

    private void Stop() => _media!.Stop();

    private void Play() => _media!.Play();

    private void Pause() => _media!.Pause();

    private async Task IsPlaying(long sid, long fid)
    {
        bool res = _media!.CurrentState == MediaElementState.Playing;
        await SendAction("onIsPlaying", sid, fid, res.ToAny());
    }

    private async Task Duration(long sid, long fid)
    {
        int duration = _media!.Duration.Milliseconds;
        await SendAction("onDuration", sid, fid, duration.ToAny());
    }

    private async Task CurrentPosition(long sid, long fid)
    {
        int pos = _media!.Position.Milliseconds;
        await SendAction("onCurrentPosition", sid, fid, pos.ToAny());
    }

    private async Task IsLooping(long sid, long fid)
    {
        bool res = _media!.ShouldLoopPlayback;
        await SendAction("onIsLooping", sid, fid, res.ToAny());
    }

    private void SetLooping(params Any[] argv) =>
        _media!.ShouldLoopPlayback = argv[0].P2Bool();

    private void SetSpeed(params Any[] argv) =>
        _media!.Speed = argv[0].P2Double();

    private void SetVolume(params Any[] argv) =>
        _media!.Volume = argv[0].P2Double();

    private void SeekTo(params Any[] argv)
    {
        int ms = argv[0].P2Int();
        if (ms < 0 || ms > _media!.Duration.Milliseconds) return;
        _media!.SeekTo(TimeSpan.FromMilliseconds(ms));
    }

    private void SetOnCompletionListener()
    {
        // TODO
        static void Opened(object? s, EventArgs e) { }
        //await SendCallback(argv[0].P2Interaction());
        _media!.MediaOpened += Opened;
    }
}
